# -*- coding: utf-8 -*-
import socket #socket.*
import random #rand.randrange
import time #time.sleep
import pickle # pickle.loads pickle.dumps
#import msvcrt #getch
import os #os.system -> setter le nom du terminal
import copy #pour les copies des objets

import GLOB

class User:
	def __init__(self, name, id):
		self.name = name
		self.id = id
		self.frame = 0
		self.ping = 0
class Message:
	def __init__(self, message, userList=0): #contient la liste de recipients du message
		self.message = message
		self.userList = userList #un message qui a comme userList '0' est adressé à tout le monde
class Server:
	def __init__(self):
		self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		self.socket.setblocking(False)
		self.socket.bind(("", 41223))

		self.users = dict() #{addr:user}

		self.messages = [] # liste contenant des objets 'message' avec leur destinataires avant d'être convertis en string
		self.exit = False #un status pour savoir si oui ou non on veut quitter le serveur
		self.partieEnCours = False #pour le système de lobby
		self.currentLoop = 0
		self.refFrame = 0

		self.nombreMessagesEnvoyes = 0 #variable debug: nombre de donnes envoyes
		self.nombreMessagesRecus = 0 #variable debug: nombre de donnes envoyes
	def ajoutMessage(self, typeMessage, info=0, userList=0, messageChat = ""): #le parametre info passe un eventuel parametre requis
		self.nombreMessagesEnvoyes += 1
		texteDuMessage = ""
		if typeMessage == "Connexion": # info -> nom
			texteDuMessage += "#CON:" + str(info)
		elif typeMessage == "Message":
			texteDuMessage+= "#MES:" + messageChat
		elif typeMessage == "Demarrage":
			texteDuMessage += "#DEM:"
			texteDuMessage += str(GLOB.currentJoueur-1)
			#Ajout éventuel d'un seed commun pour la gestion de L'IA
		elif typeMessage == "Acknowledge":
			texteDuMessage += "#ACK:" + str(info)
		elif typeMessage == "Deconnexion": # info -> nom
			texteDuMessage += "#DEC:" + str(info)
		elif typeMessage == "Attack":
			texteDuMessage += "#ATT:" + str(info)
		else:
			print("Message non implementer :", typeMessage, " INFO: ", info)
		self.messages.append(Message(texteDuMessage, userList)) #on ajoute le message à la queue d'envoi
	def receive(self):
		if self.socket: #on verifie que le socket n'est pas detruit
			try:
				data, addr = self.socket.recvfrom(1024) #1024 bytes; 1KB
				dataString = pickle.loads(data) # "#doodle#doodle2#doodle3"
				dataList = dataString.split("#") # ("doodle", "doodle2", "doodle3")
				#ici c'etait theoriquement la premiere connection, mais on handle ca dans le message 'connection'
				userList = list()
				for i in self.users.keys():
					userList.append(i)
				for message in dataList:
					header = "[" + str(time.strftime('%X')) + "] " #permet l'acces a un formatage rudimentaire
					if message.find("CON") > -1:
						nomJoueur = message[4:]
						#self.ajoutMessage("Connexion", nomJoueur)
						nouvelUtilisateur = User(nomJoueur, GLOB.currentJoueur)
						self.users.update({addr:nouvelUtilisateur})
						self.ajoutMessage("Acknowledge", info=GLOB.currentJoueur, userList=[addr])
						self.ajoutMessage("Connexion", info = GLOB.currentJoueur)
						GLOB.currentJoueur += 1
						print(header + "CONNEXION: " + nomJoueur + " - " + str(addr))
					elif message.find("MES")> -1:
						message = message[4:]
						self.ajoutMessage("Message",info = 0 , userList=0, messageChat = message)
					elif message.find("DEM")> -1:
						self.partieEnCours = True
					if self.partieEnCours:
						if message.find("DEC") > -1:
							nomJoueur = message[4:]
							print(header + "DECONNEXION: " + nomJoueur + " - " + str(addr))
							self.ajoutMessage("Deconnexion", info=nomJoueur)
							self.users.pop(addr)
						elif message.find("FER") > -1:
							print(header + "FERMETURE: Fermeture du serveur")
							self.exit = True
							raise socket.error #sort de la boucle de reception
						elif message.find("FRA") > -1:
							message = message[4:]
							#update la frame connu du joueur pour la plus recente recue
							self.users[addr].frame = int(message)
							self.messages.append(Message("#LOG:",userList))
							#ajoute une frame de reference commune pour les autres joueurs
						elif message.find("ACT") > -1:
							self.messages.append(Message("#"+message,userList))
						elif message.find("PIC") > -1:
							self.messages.append(Message("#"+message))
						elif message.find("ATT") > -1: #Traitement des attaques!
							message = message[4:]
							self.ajoutMessage("Attack", message)
							#ici il faut faire l'evoi de la distance entre la joueur et la cible au model du joueur(code de fatou) mais il manque la distance dans les info du message
						else: #message de type inconnu
							pass #print(message)
			except socket.error: #exception, on ne recoit plus rien
				pass
				#break #on sort du loop
	def send(self):
		if self.partieEnCours == True:
			self.refFrame = 0
			for user in self.users.values():
				if user.frame > self.refFrame:
					self.refFrame = user.frame
				#print("FRAME DE "+user.name+ " : "+ str(user.frame))
			self.refFrame += 1#Déterminer le Frame de Référence
			#print("FRAME REFERENCE :",self.refFrame)
		if self.socket: #on verifie que le socket n'est pas detruit
			messagePourTous = ""
			for message in self.messages: 
				if message.userList == 0: #si le message est pour tous le monde
					messagePourTous += message.message
			for client in self.users.keys(): #pour chacun des utilisateurs:
				messageParticulier = copy.copy(messagePourTous) #un message particulier commence par le message de tout le monde
				for message in self.messages:
					if message.userList != 0: #si on a des destinataires
						if client in message.userList: #si le client est dans la liste d'envoi du message
							messageParticulier += message.message
				if(len(messageParticulier) is not 0):
					#print("DONNEES SERVEUR",messageParticulier)
					self.socket.sendto(pickle.dumps("#FRA:" + str(self.users[client].frame)+"#FRR:"+str(self.refFrame)+messageParticulier), client)#envoi de la liste des adresse a tous les clients
			self.messages = [] #on clear la liste d'envoi
	def quit(self):
		self.ajoutMessage(Fermeture(time.strftime('%X'))) #envoi du message de fermeture aux clients
		self.send()
		self.socket.close() #on ferme le socket
		self.socket = 0 #destruction du scoket
		print("Serveur correctement ferme")


	def mainLoop(self):
		print("Serveur lance")
		#os.system('title Serveur')
		while not self.partieEnCours: #on attend que les joueurs join
			self.receive()
			self.send()
		print("GO: La partie commence!")
		self.ajoutMessage("Demarrage")
		self.send()
		while not self.exit:
			self.receive()
			self.send()
			#if self.currentLoop % 100 == 0:
				#print("LOOP: " + str(self.currentLoop))
				#pass
			#self.currentLoop += 1
		self.quit()
	
if __name__ == "__main__":
	server = Server()
	server.mainLoop()
